home *** CD-ROM | disk | FTP | other *** search
- /* Copyright (C) 2002 Tom Parker (tom@carrott.org),
- Matthias Münch (matthias@amigaworld.de)
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-
- In addition, as a special exception, Tom Parker and Matthias Münch give
- permission to link the code of this program with a TCP stack of your
- choice, any official MUI libraries or classes and any custom MUI classes
- that should be necessary for the operation of this program. This
- exception also gives you permission to distribute linked combinations
- including this software with any of the before-mentioned libraries and
- classes. You must obey the GNU General Public License in all respects for
- all of the code used other than that provided by the before-mentioned
- libraries and classes. As part of this exception you are obliged to
- follow the license terms of the before-mentioned libraries, this license
- does not compel you to follow those terms, but if you do not then you may
- not link with those libraries. If you modify this file, you may extend
- this exception to your version of the file, but you are not obligated to
- do so. If you do not wish to do so, delete this exception statement from
- your version.
- */
- /*
- ** roster
- */
-
- #include "common.h"
-
- #include <stdlib.h>
- #include <string.h>
-
- #include <MUI/NListview_mcc.h>
-
- #include "pix/unavail.h"
- #include "pix/avail.h"
- #include "pix/chat.h"
- #include "pix/away.h"
- #include "pix/xaway.h"
- #include "pix/dnd.h"
- #include "pix/msg.h"
- #include "pix/error.h"
-
- static Object *r_list = NULL;
-
- static ULONG roster_new(struct IClass *cl, Object *obj, struct opSet *msg);
- static ULONG roster_dispose(struct IClass *cl,Object *obj,Msg msg);
- static MUI_LIST_DISP_DECL(roster_disp, juser u);
- static MUI_LIST_DEST_DECL(roster_dest, juser u);
- static MUI_LIST_COMP_DECL(roster_comp, juser u1, juser u2);
- static void roster_menu(struct rosterdata *data, int menu);
- static void roster_update_item(iks *x);
- static void roster_close_all_windows();
- static void roster_close_windows(juser u);
- static void roster_remove_window(Object *win);
-
- MUI_DISPATCH(roster_dispatch)
- {
- switch(msg->MethodID)
- {
- case OM_NEW:
- return roster_new(cl, obj, (APTR)msg);
-
- case OM_DISPOSE:
- return roster_dispose(cl, obj, (APTR)msg);
-
- case ROSTER_MENU:
- roster_menu(INST_DATA(cl,obj), (int)MARG1);
- return 0;
-
- case ROSTER_DOUBLECLICK:
- {
- juser u;
- u_long tmp;
- struct rosterdata *data = INST_DATA(cl,obj);
- GetAttr(MUIA_NList_DoubleClick, obj, &tmp);
- if(tmp == -1 || tmp == -2) return(0);
- DoMethod(obj, MUIM_NList_GetEntry, tmp, &u);
- if(!u) {
- return(0);
- }
- /* FIX ME: different actions for different events */
- if(u->event) {
- read_from(u);
- } else {
- write_to(iks_id_print(u->id), NULL, NULL);
- }
- return 0;
- }
-
- case ROSTER_CLOSEME:
- {
- Object *win = (Object *)MARG1;
- DoMethod(gui.app, OM_REMMEMBER, win);
- MUI_DisposeObject(win);
- return 0;
- }
-
- }
- return DoSuperMethodA(cl,obj,msg);
- }
-
-
- static ULONG roster_new(struct IClass *cl, Object *obj, struct opSet *msg)
- {
- static struct Hook dispHook = { {0,0}, &roster_disp, NULL, NULL };
- static struct Hook destHook = { {0,0}, &roster_dest, NULL, NULL };
- static struct Hook compHook = { {0,0}, &roster_comp, NULL, NULL };
- struct rosterdata *data;
-
- obj = (Object *)DoSuperNew(cl,obj,
- MUIA_ContextMenu, NULL,
- InputListFrame,
- MUIA_NList_Format, ",",
- MUIA_NList_ForcePen, MUIV_NList_ForcePen_On,
- MUIA_NList_DisplayHook, &dispHook,
- MUIA_NList_DestructHook, &destHook,
- MUIA_NList_CompareHook, &compHook,
- MUIA_CycleChain, 1,
- TAG_MORE, msg->ops_AttrList);
-
- if(!obj) return(0);
-
- data = INST_DATA(cl,obj);
- r_list = obj;
- data->list = obj;
- data->popupmenu = NULL;
-
- data->unavail_pix = mui_pix("PROGDIR:Images/unavail.iff", &unavail_defpix, MUIA_Bitmap_Transparent, 0, TAG_DONE);
- data->avail_pix = mui_pix("PROGDIR:Images/avail.iff", &avail_defpix, MUIA_Bitmap_Transparent, 0, TAG_DONE);
- data->chat_pix = mui_pix("PROGDIR:Images/chat.iff", &chat_defpix, MUIA_Bitmap_Transparent, 0, TAG_DONE);
- data->away_pix = mui_pix("PROGDIR:Images/away.iff", &away_defpix, MUIA_Bitmap_Transparent, 0, TAG_DONE);
- data->xaway_pix = mui_pix("PROGDIR:Images/xaway.iff", &xaway_defpix, MUIA_Bitmap_Transparent, 0, TAG_DONE);
- data->dnd_pix = mui_pix("PROGDIR:Images/dnd.iff", &dnd_defpix, MUIA_Bitmap_Transparent, 0, TAG_DONE);
- data->msg_pix = mui_pix("PROGDIR:Images/msg.iff", &msg_defpix, MUIA_Bitmap_Transparent, 0, TAG_DONE);
- data->error_pix = mui_pix("PROGDIR:Images/error.iff", &error_defpix, MUIA_Bitmap_Transparent, 0, TAG_DONE);
-
- DoMethod(obj, MUIM_NList_UseImage, data->unavail_pix, 0, 0L);
- DoMethod(obj, MUIM_NList_UseImage, data->avail_pix, 1, 0L);
- DoMethod(obj, MUIM_NList_UseImage, data->chat_pix, 2, 0L);
- DoMethod(obj, MUIM_NList_UseImage, data->away_pix, 3, 0L);
- DoMethod(obj, MUIM_NList_UseImage, data->xaway_pix, 4, 0L);
- DoMethod(obj, MUIM_NList_UseImage, data->dnd_pix, 5, 0L);
- DoMethod(obj, MUIM_NList_UseImage, data->msg_pix, 6, 0L);
- DoMethod(obj, MUIM_NList_UseImage, data->error_pix, 7, 0L);
-
- DoMethod(obj, MUIM_Notify, MUIA_NList_DoubleClick, MUIV_EveryTime, obj, 1, ROSTER_DOUBLECLICK);
-
-
- return (ULONG)obj;
- }
-
-
- MUI_LIST_DISP_STATIC(roster_disp, juser u)
- {
- if(u->event)
- {
- *array++ = "\33o[6]";
- } else if (u->error) {
- *array++ = "\33o[7]";
- } else if(u->subs != JUSER_NONE) {
- switch(u->show)
- {
- case IKS_SHOW_UNAVAILABLE: *array++ = "\33o[0]"; break;
- case IKS_SHOW_AVAILABLE: *array++ = "\33o[1]"; break;
- case IKS_SHOW_CHAT: *array++ = "\33o[2]"; break;
- case IKS_SHOW_AWAY: *array++ = "\33o[3]"; break;
- case IKS_SHOW_XA: *array++ = "\33o[4]"; break;
- case IKS_SHOW_DND: *array++ = "\33o[5]"; break;
- case IKS_SHOW_ERROR: *array++ = "\33o[7]"; break;
- default:
- *array++ = "\33o[1]"; break;
- }
- }
- else
- {
- *array++ = "";
- }
-
- if(u->name) {
- *array = u->name;
- } else {
- /** @todo use new pool functions and use iks_pool_free */
- static char *s = NULL;
-
- if (s) {
- free(s);
- }
-
- *array = s = strdup(convert_locale(iks_id_print(u->id)));
- }
-
- {
- static char style[10];
- char styleindex = 0;
-
- style[0] = 0;
- if (u->error) {
- strcpy(style, MUIX_PH);
- }
- if (u->subs != JUSER_BOTH) {
- strcpy(style + strlen(style), MUIX_I);
- }
- if (u->ask) {
- strcpy(style + strlen(style), MUIX_B);
- }
- array[DISPLAY_ARRAY_MAX] = style;
- }
- return 0;
- }
-
-
- MUI_LIST_DEST_STATIC(roster_dest, juser u)
- {
- if(!u) return(0);
- iks_pool_delete(u->p);
- return(0);
- }
-
-
- MUI_LIST_COMP_STATIC(roster_comp, juser u1, juser u2)
- {
- if(u1->event && !u2->event) return(-1);
- if(!u1->event && u2->event) return(1);
-
- if(u1->subs == JUSER_NONE && u2->subs != JUSER_NONE) return(1);
- if(u1->subs != JUSER_NONE && u2->subs == JUSER_NONE) return(-1);
-
- if(u1->show == IKS_SHOW_UNAVAILABLE && u2->show != IKS_SHOW_UNAVAILABLE) return(1);
- if(u1->show != IKS_SHOW_UNAVAILABLE && u2->show == IKS_SHOW_UNAVAILABLE) return(-1);
-
- if(u1->show > u2->show) return(1);
- if(u2->show > u1->show) return(-1);
-
- if(!u1->name && u2->name) return(1);
- if(u1->name && !u2->name) return(-1);
-
- if(!u1->name && !u2->name)
- {
- int i = iks_strcmp(u1->id->server, u2->id->server);
- if(i != 0) return(i);
- iks_strcmp(u1->id->user, u2->id->user);
- }
-
- return(iks_strcmp(u1->name, u2->name));
- }
-
-
- static ULONG roster_dispose(struct IClass *cl,Object *obj,Msg msg)
- {
- struct rosterdata *data = INST_DATA(cl,obj);
-
- if(data->unavail_pix) MUI_DisposeObject(data->unavail_pix);
- if(data->avail_pix) MUI_DisposeObject(data->avail_pix);
- if(data->chat_pix) MUI_DisposeObject(data->chat_pix);
- if(data->away_pix) MUI_DisposeObject(data->away_pix);
- if(data->xaway_pix) MUI_DisposeObject(data->xaway_pix);
- if(data->dnd_pix) MUI_DisposeObject(data->dnd_pix);
- if(data->msg_pix) MUI_DisposeObject(data->msg_pix);
- if(data->error_pix) MUI_DisposeObject(data->error_pix);
-
- return(DoSuperMethodA(cl, obj, msg));
- }
-
-
- static void roster_menu(struct rosterdata *data, int menu)
- {
- juser u;
- iks *x;
-
- DoMethod(r_list, MUIM_NList_GetEntry, MUIV_NList_GetEntry_Active, &u);
-
- if(!u && menu == ROSTER_SEND) xfer_test();
-
- if(!u) return;
-
- switch(menu)
- {
- case ROSTER_READ:
- read_from(u);
- break;
-
- case ROSTER_CHAT:
- chat_with(u);
- break;
-
- case ROSTER_INFO:
- userinfo_of(u);
- break;
-
- case ROSTER_SEND:
- xfer_share(u);
- break;
-
- case ROSTER_ACCEPT:
- x = iks_make_pres(IKS_TYPE_SUBSCRIBED, 0, iks_id_printx(u->id, IKS_ID_USER | IKS_ID_SERVER), NULL);
- iks_send(net.parser, x);
- iks_delete(x);
- break;
-
- case ROSTER_SUBS:
- // if the contact is a transport don't subscribe
- if (u->id->user == NULL) {
- break;
- }
- x = iks_make_pres(IKS_TYPE_SUBSCRIBE, 0, iks_id_printx(u->id, IKS_ID_USER | IKS_ID_SERVER), NULL);
- iks_send(net.parser, x);
- iks_delete(x);
- break;
-
- case ROSTER_REMOVE:
- roster_remove(u);
- break;
-
- }
- }
-
-
- void roster_fetch(void)
- {
- iks *x;
-
- x = iks_make_iq(IKS_TYPE_GET, IKS_NS_ROSTER);
- iks_send(net.parser, x);
- iks_delete(x);
- }
-
-
- void roster_reset(void)
- {
- roster_close_all_windows();
- DoMethod(r_list, MUIM_NList_Clear);
- }
-
-
- void roster_update(ikspak *pak)
- {
- iks *x;
-
- con_debug("processing roster update");
-
- switch(pak->subtype)
- {
- case IKS_TYPE_RESULT: // I think this should only happen when we go online
- net.state = NET_ON;
- gui_state(MSG_PRES_ONLINE);
- x = iks_make_pres(IKS_TYPE_AVAILABLE, IKS_SHOW_AVAILABLE, NULL, NULL);
- iks_send(net.parser, x);
- iks_delete(x);
-
- case IKS_TYPE_SET:
- x = iks_child(pak->query);
- while(x)
- {
- if(iks_strcmp(iks_name(x), "item")==0)
- roster_update_item(x);
- x = iks_next(x);
- }
- iks_delete(pak->x);
- break;
-
- default:
- con_debug("parse roster failed\n");
- iks_delete(pak->x);
- }
- }
-
-
- static void roster_update_item(iks *x)
- {
- juser u;
- ikspool *p;
- iksid *id;
- char *tmp;
-
- tmp = iks_find_attrib(x, "jid");
- if(!tmp) return;
-
- p = iks_pool_new(512);
- if(!p) return;
- id = iks_id_new(p, tmp);
-
- u = roster_find(id);
- if(u)
- {
- iks_pool_delete(p);
- p = u->p;
- }
- else
- {
- u = iks_pool_alloc(p, sizeof(juser_struct));
- if(!u) return;
- memset(u, 0, sizeof(juser_struct));
- u->p = p;
-
- u->id = id;
- DoMethod(r_list, MUIM_NList_InsertSingle, u, MUIV_NList_Insert_Sorted);
- }
-
- tmp = iks_find_attrib(x, "name");
- if(tmp) {
- u->name = iks_pool_strdup(p, convert_locale(tmp));
- }
-
- u->ask = iks_find_attrib(x, "ask");
-
- tmp = iks_find_attrib(x, "subscription");
- if(tmp)
- {
- if(strcmp(tmp, "none")==0)
- u->subs = JUSER_NONE;
- else if(strcmp(tmp, "to")==0)
- u->subs = JUSER_TO;
- else if(strcmp(tmp, "from")==0)
- u->subs = JUSER_FROM;
- else if(strcmp(tmp, "both")==0)
- u->subs = JUSER_BOTH;
- else if(strcmp(tmp, "remove")==0)
- {
- long pos = MUIV_NList_GetPos_Start;
- DoMethod(r_list, MUIM_NList_GetPos, u, &pos);
- if(pos!=MUIV_NList_GetPos_End)
- DoMethod(r_list, MUIM_NList_Remove, pos);
- return;
- }
- }
-
- u->group = iks_strdup(iks_find_cdata(x, "group"));
- if (u->group == 0) {
- u->group = "";
- }
-
- roster_refresh(u);
- }
-
-
- void roster_update_presence(ikspak *pak)
- {
- juser u;
- char *tmp;
-
- u = roster_find(pak->from);
- if (!u) {
- char buff[100];
- snprintf(buff, 99, "got presence packet for user not on roster, user is '%s'",
- pak->from ? iks_id_print(pak->from) : "null");
- con_debug(buff);
- return;
- }
-
- u->show = pak->show;
- tmp = iks_find_cdata(pak->x, "status");
- if(tmp) u->status = iks_pool_strdup(u->p, tmp);
- if(pak->from->resource)
- {
- u->id->resource = iks_pool_strdup(u->p, pak->from->resource);
- u->id->full = NULL;
- }
-
- if (iks_strcmp(iks_find_attrib(pak->x, "type"), "error") == 0) {
- u->error = "error";
- } else {
- u->error = NULL;
- }
-
- roster_refresh(u);
-
- iks_delete(pak->x);
- }
-
-
- juser roster_find(iksid *id)
- {
- juser u, u2 = NULL;
- int i=0;
-
- while(1)
- {
- DoMethod(r_list, MUIM_NList_GetEntry, i, &u);
- if(!u) break;
- if(iks_id_cmp(id, u->id) == 0) return(u);
- if(!u2 && iks_id_cmpx(id, u->id, IKS_ID_USER | IKS_ID_SERVER) == 0) u2 = u;
- i++;
- }
- if(u2) return(u2);
-
- return NULL;
- }
-
-
- juser roster_next(int i)
- {
- juser u;
-
- DoMethod(r_list, MUIM_NList_GetEntry, i, &u);
- return u;
- }
-
-
- juser roster_locate(iksid *id)
- {
- juser u;
-
- u = roster_find(id);
- if(!u)
- {
- ikspool *p;
- p = iks_pool_new(512);
- if(!p) return(NULL);
- u = iks_pool_alloc(p, sizeof(juser_struct));
- if(!u) return(NULL);
- memset(u, 0, sizeof(juser_struct));
- u->p = p;
- u->id = id;
- DoMethod(r_list, MUIM_NList_InsertSingle, u, MUIV_NList_Insert_Sorted);
- }
-
- return u;
- }
-
-
- void roster_remove(juser u)
- {
- con_debug("doing remove");
-
- roster_close_windows(u);
- {
- int parts = 0;
- iks *x, *y;
- x = iks_make_iq(IKS_TYPE_SET, IKS_NS_ROSTER);
- y = iks_find(x, "query");
- y = iks_insert(y, "item");
-
- // transports should have a resource, users, not
- if (u->id->user == NULL) {
- parts = IKS_ID_USER | IKS_ID_SERVER | IKS_ID_RESOURCE;
- } else {
- parts = IKS_ID_USER | IKS_ID_SERVER;
- }
- iks_insert_attrib(y, "jid", iks_id_printx(u->id, parts));
- iks_insert_attrib(y, "subscription", "remove");
- iks_send(net.parser, x);
- iks_delete(x);
-
- /* since we are now adding contacts without a resource this section */
- /* should no longer be needed and was causing unusual responses */
- /* from the server which resulted in enforcer hits from the iksemel */
- /* library, it has now been removed */
- /* will remove altogether when I have looked into the iksemel hits */
-
- /* we remove any accidentally added contacts with a resource, still unsure how this happens */
- /* the correct solution here is if there are two entries in the roster with the same jid */
- /* but different reasources, they should be treated seperately, and then remove only */
- /* the specified contact */
- /* x = iks_make_iq(IKS_TYPE_SET, IKS_NS_ROSTER);
- y = iks_find(x, "query");
- y = iks_insert(y, "item");
- iks_insert_attrib(y, "jid", iks_id_printx(u->id, IKS_ID_USER | IKS_ID_SERVER | IKS_ID_RESOURCE));
- iks_insert_attrib(y, "subscription", "remove");
- iks_send(net.parser, x);
- iks_delete(x);
- */ }
- }
-
-
- void roster_refresh(juser u)
- {
- long pos = MUIV_NList_GetPos_Start;
-
- DoMethod(r_list, MUIM_NList_Sort);
- if(u)
- {
- DoMethod(r_list, MUIM_NList_GetPos, u, &pos);
- if(pos != MUIV_NList_GetPos_End)
- DoMethod(r_list, MUIM_NList_Redraw, pos);
- }
- }
-
- /**
- * closes all windows associated with the roster
- */
- void roster_close_all_windows() {
- int tmp = 0;
- int entry;
-
- get(r_list, MUIA_NList_Entries, &tmp);
- if (tmp > 0) {
- for (entry = 0; entry < tmp; entry++) {
- roster_close_windows(roster_next(entry));
- }
- }
- }
-
- /**
- * close and dispose of any windows associated with the passed user
- */
- void roster_close_windows(juser u) {
-
- if (u->infowin) {
- roster_remove_window(u->infowin);
- u->infowin = NULL;
- }
- if (u->readwin) {
- roster_remove_window(u->readwin);
- u->readwin = NULL;
- }
-
- if (u->chatwin) {
- roster_remove_window(u->chatwin);
- u->chatwin = NULL;
- }
- }
-
- /**
- * dispose of the specified window
- */
- void roster_remove_window(Object *win)
- {
- DoMethod(gui.app, OM_REMMEMBER, win);
- MUI_DisposeObject(win);
- return;
- }
-